(function(){
	var util = require('util');
	var Bookshelf = require('bookshelf');
	var db = require('bookshelf').bookshelf;
	var Database = module.exports = {};
	var successHanlder = function(){};
	var failureHandler = function(errors){};
	var tables = new Array();
	var errors = {Count : 0, Messages: new Array(), add: function(msg){ Count++; Messages.push(msg); }};
	var initializing = 0;
	
	Database.Models = {};
	Database.Collections = {};
	
    log = function(msg) {
        //console.log(new Date().toLocaleTimeString() + ' [database]: ' + msg);
    };

	Database.initialize = function(success,failure){
		// log('database.js: starting initialization');
		if (success != null){
			successHanlder = success;
		};
		if (failure != null){
			failureHandler = failure;
		};
		if (db != null) {
			log('database.js: creating tables');
			CreateTables();
		};
	};
	
	Database.createTestData = function(callback){
		if (db == null || tables.length <= 0) {
			callback({Count: 1, Messages:['Database is not initialized']});
		} else {
			// log('database.js: creating test data');
            new Database.Models.User({user_id : 1})
		    .fetch({require: false})
		    .then(function(resp) {
			    if (resp == null) {
			        var user = null;
			        user = new Database.Models.User({login:'admin',password:'test',role:8,lang:'en'});
			        user.save().then(function(model) {
					        // log(util.inspect(model, false, null));
				        });
			        user = new Database.Models.User({login:'owner',password:'test',role:4,lang:'fr'});
			        user.save().then(function(model) {
					        // log(util.inspect(model, false, null));
				        });
			        user = new Database.Models.User({login:'user',password:'test',role:2,lang:'en'});
			        user.save().then(function(model) {
					        // log(util.inspect(model, false, null));
				        });
			        user = new Database.Models.User({login:'registered',password:'test',role:1,lang:'fr'});
			        user.save().then(function(model) {
					        // log(util.inspect(model, false, null));
				        });
			
			        var server = null;
			        server = new Database.Models.Server({
                                        name: 'public', 
                                        path:'C:\\_NO_BACKUP_\\server\\', 
                                        executable:'arma3server.exe', 
                                        config_path:'C:\\_NO_BACKUP_\\server\\configs\\',
                                        template_path:'C:\\_NO_BACKUP_\\server\\templates\\',
                                        is_started: false,
                                        is_stopped: true,
                                        is_starting: false,
                                        is_stopping: false,
                                        is_updating: false, 
                                        });
			        server.save().then(function(model) {
					        log(util.inspect(model, false, null));
					        var template = new Database.Models.Template({
                                        server_id: model.server_id,
                                        file:'server.tpl',
                                        destination:'server.cfg',
                                        is_commandline: false,
                                        is_active: true,
                                        });
					        template.save().then();
					        var config = null;
					        config = new Database.Models.Config({server_id: model.server_id, name:'serverName', desc:'The name of the server displayed in the server list', value:'[FR] [V]Vétérans - Public'});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'serverPassword', desc:'The password of the server used to enter it. Leave blank for no password.', value:''});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'battlEye', desc:'Whether or not BattlEye is active: 0 or 1', value:'1'});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'checkPBO', desc:'Whether or not to control the PBO files: 0 or 1 - Not editable!', value:'1', is_writable:0});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'hidden', desc:'a test field - Not visible!', value:'1', is_visible:0});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'unused', desc:'This one should be filtered out', value:'inactive', is_active:0});
					        config.save().then();
				        });
			        server = new Database.Models.Server({
                                        name: 'prive', 
                                        path:'C:\\_NO_BACKUP_\\server\\', 
                                        executable:'arma3server.exe', 
                                        config_path:'C:\\_NO_BACKUP_\\server\\configs\\',
                                        template_path:'C:\\_NO_BACKUP_\\server\\templates\\',
                                        is_started: false,
                                        is_stopped: true,
                                        is_starting: false,
                                        is_stopping: false,
                                        is_updating: false, 
                                    });
			        server.save().then(function(model) {
					        log(util.inspect(model, false, null));
					        var template = new Database.Models.Template({
                                        server_id: model.server_id,
                                        file:'server.tpl',
                                        destination:'C:\\_NO_BACKUP_\\server\\templates\\server.cfg',
                                        is_commandline: false,
                                        is_active: true,
                                        });
					        template.save().then();
					        var config = null;
					        config = new Database.Models.Config({server_id: model.server_id, name:'serverName', desc:'The name of the server displayed in the server list', value:'[FR] [V]Vétérans - Public'});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'serverPassword', desc:'The password of the server used to enter it. Leave blank for no password.', value:''});
					        config.save().then();
					        config = new Database.Models.Config({server_id: model.server_id, name:'battlEye', desc:'Whether or not BattlEye is active: 0 or 1', value:'1'});
					        config.save().then();
				        });
                };
			    callback(null);
		    });
		};
	};
	

	// --------------------------------------------------------------------------------------------
	//									DB SCHEMA
	// --------------------------------------------------------------------------------------------
	

	// ------------------------ USERS TABLE ------------------------
	tables.push({
		Name:'users',
		Create:function(t){
			t.increments('user_id').primary();
			t.string('login').unique();		// The login used to connect
			t.string('password');			// ...
			t.integer('group_id');			// group the user belongs to
			t.integer('role');				// The id of the role defined as: 0: anonymous, 1: registered, 2: user, 4: owner, 8: admin
			t.string('email');				// For automatic email notifications (later)
			t.string('lang');				// user language
			t.boolean('is_active'); 			// Whether or not the account is active
			t.integer('pwd_fails'); 			// Each time an invalid password is given, the amout increases, at the configured point, the account is blocked.
			t.boolean('is_blocked'); 		// Whether or not the account is blocked
			t.dateTime('blocked_datetime');	// The datetime when it was last blocked, resets the possibility after 24hours
			t.string('last_ip');				// Last IP used to connect
			t.dateTime('last_connection');	// DateTime of the last successful connection
			t.timestamps();					// Internal timestamps to track changes
		},
	});
	
	Database.Models.User = db.Model.extend({
		tableName: 'users',
		idAttribute: 'user_id',
		initialize: function(obj, args) {
		},
        cleanUp: function() {
            if (this != null) {
			    this.unset({
                    email: '',
                    password: '',
                    pwd_fails: 0,
                    blocked_datetime: null,
                    last_ip: null,
                    last_connection: null,
                }, { silent: true });
            };
        },
	});
	
	Database.Collections.Users = db.Collection.extend({
		model: Database.Models.User
	}); 

	// ------------------------ GROUPS TABLE ------------------------
	tables.push({
		Name:'groups',
		Create:function(t){
			t.increments('group_id').primary();
			t.string('group_name').unique();		// The login used to connect
			t.string('desc');			// ...
			t.boolean('is_active'); 			// Whether or not the group is active
			t.timestamps();					// Internal timestamps to track changes
		},
	});
	
	Database.Models.Group = db.Model.extend({
		tableName: 'groups',
		idAttribute: 'group_id',
		Users: function() {
			return this.hasMany(Database.Models.User,'group_id');
		},
		initialize: function(obj, args) {
		},
	});
	
	Database.Collections.Groups = db.Collection.extend({
		model: Database.Models.Group,
	}); 

	// ------------------------ LOGS TABLE ------------------------
	tables.push({
		Name:'logs',
		Create:function(t){
			t.bigIncrements('log_id').primary();
			t.integer('user_id');			// User who performed the action
			t.string('module');				// The module in which the action is done
			t.string('action');				// The type of the action
			t.string('data');				// The data of the action
			t.dateTime('log_datetime');		// The datetime when it was last blocked, resets the possibility after 24hours
		},
	});
	
	Database.Models.Log = db.Model.extend({
		tableName: 'logs',
		idAttribute: 'log_id',
		initialize: function(obj, args) {
		},
	});
	
	Database.Collections.Logs = db.Collection.extend({
		model: Database.Models.Log,
	}); 

	// ------------------------ ERRORS TABLE ------------------------
	tables.push({
		Name:'errors',
		Create:function(t){
			t.bigIncrements('error_id').primary();
			t.integer('user_id');			// User who performed the action
			t.string('module');				// The module in which the error is done
			t.string('action');				// The type of the error
			t.string('data');				// The data of the error
			t.dateTime('error_datetime');	// The datetime when it was last blocked, resets the possibility after 24hours
		},
	});
	
	Database.Models.Error = db.Model.extend({
		tableName: 'errors',
		idAttribute: 'error_id',
		initialize: function(obj, args) {
		},
	});
	
	Database.Collections.Errors = db.Collection.extend({
		model: Database.Models.Error,
	}); 

	// ------------------------ TEMPLATES TABLE ------------------------
	tables.push({
		Name:'templates',
		Create:function(t){
			t.increments('template_id').primary();
			t.integer('server_id');			// The server this template applies to
			t.string('file');				// The name of the template file to parse for this server 
			t.string('destination');		// The name and path of the destination parsed file 
			t.string('path_override');		// The path where the file should be copied when being used
			t.boolean('is_commandline');	// Whether or not the template is used to launch the server executable
			t.boolean('is_active'); 		// Whether or not the template has to be parsed
			t.timestamps();					// Internal timestamps to track changes
		},
	});
	
	Database.Models.Template = db.Model.extend({
		tableName: 'templates',
		idAttribute: 'template_id',
	});
	
	Database.Collections.Templates = db.Collection.extend({
		model: Database.Models.Template,
	}); 

	// ------------------------ CONFIGS TABLE ------------------------
	tables.push({
		Name:'configs',
		Create:function(t){
			t.bigIncrements('config_id').primary();
			t.integer('server_id');			// The server this config applies to
			t.string('name');				// The name of the config value 
			t.string('desc');				// The description of the config value 
			t.string('value');				// The value of the config 
			t.boolean('is_visible'); 		// Whether or not the value is shown
			t.boolean('is_writable'); 		// Whether or not the value is writable
			t.boolean('is_active'); 			// Whether or not the value is used
			t.timestamps();					// Internal timestamps to track changes
		},
	});
	
	Database.Models.Config = db.Model.extend({
		tableName: 'configs',
		idAttribute: 'config_id',
	});
	
	Database.Collections.Configs = db.Collection.extend({
		model: Database.Models.Config,
		updateAll: function(callback) {
			var collection = this;
			options = {success: callback, method: 'update'};
			return this.sync(this, options);
		},
	}); 

	// ------------------------ SERVERS TABLE ------------------------
	tables.push({
		Name:'servers',
		Create:function(t){
			t.increments('server_id').primary();
			t.integer('group_id');			// User group who can perform all actions, leave null if all users can access it
			t.string('name').unique();				// The name of the server to display in the web interface
			t.string('path');				// The path where the server root it
			t.string('executable');			// The name of the server executable
			t.string('config_path');			// The path to the configuration files
			t.string('template_path');			// The path to the configuration files
			t.integer('server_port');			// The port used by the server
			t.boolean('is_starting'); 		// Whether or not the server is currently starting
			t.boolean('is_stopping'); 		// Whether or not the server is currently stopping
			t.boolean('is_updating'); 		// Whether or not the server is currently updating
			t.boolean('is_started'); 		// Whether or not the server should be started if it isn't or stopped if it's started
			t.boolean('is_stopped'); 		// Whether or not the server is currently stopped
			t.dateTime('last_datetime');		// The datetime when it was last started or stopped
			t.timestamps();					// Internal timestamps to track changes
		},
	});
	
	Database.Models.Server = db.Model.extend({
		tableName: 'servers',
		idAttribute: 'server_id',
		initialize: function(obj, args) {
            if (obj) {
			    obj.is_started = obj.is_started || false;
			    obj.is_stopping = obj.is_stopping || false;
			    obj.is_updating = obj.is_updating || false;
            };
		},
		configs: function() {
			return this.hasMany(Database.Models.Config,'server_id');
		},
		templates: function() {
			return this.hasMany(Database.Models.Template,'server_id');
		},
		memberrights: function() {
			return this.hasMany(Database.Models.MemberRight,'server_id');
		},
	});
	
	Database.Collections.Servers = db.Collection.extend({
		model: Database.Models.Server,
	}); 

	// ------------------------ ACTIONS TABLE ------------------------
	tables.push({
		Name:'actions',
		Create:function(t){
			t.increments('action_id').primary();
			t.integer('server_id');			// The server on which the action should happen
			t.integer('user_id');			// The user who triggered the action
			t.string('action');				// The action to perform
			t.string('status');			// The result of the action
			t.boolean('is_success'); 		// Whether or not the action succeeded
			t.boolean('is_executed'); 		// Whether or not the action has finished (failure or success)
			t.boolean('is_processing'); 		// Whether or not the action is being processed
			t.dateTime('action_datetime');		// The datetime when the action was added
			t.dateTime('start_datetime');		// The datetime when the action was started
			t.dateTime('end_datetime');		// The datetime when the action has ended
		},
	});
	
	Database.Models.Action = db.Model.extend({
		tableName: 'actions',
		idAttribute: 'action_id',
		initialize: function(obj, args) {
            if (obj) {
			    obj.status = obj.status || '';
			    obj.is_success = obj.is_success || false;
			    obj.is_executed = obj.is_executed || false;
			    obj.is_processing = obj.is_processing || false;
			    obj.action_datetime = obj.action_datetime || new Date();
			    log('actions.initialize:' + util.inspect(obj,false,null));
            };
		},
		user: function() {
			return this.belongsTo(Database.Models.User,'user_id');
		},
	});
	
	Database.Collections.Actions = db.Collection.extend({
		model: Database.Models.Action,
	}); 

	// ------------------------ PLAYERS TABLE ------------------------
	tables.push({
		Name:'players',
		Create:function(t){
			t.increments('player_id').primary();
			t.string('guid').unique();			// The guid of the player
			t.string('gameid').unique();			// The game id of the player
			t.string('player_name');			// The name used by the player
			t.integer('last_server');			// The last server he connected on
			t.dateTime('last_datetime');		// The datetime when he last connected
			t.string('last_ip');		// The last ip he used for connecting
			t.integer('connection_count');		// The last ip he used for connecting
		},
	});
	
	Database.Models.Player = db.Model.extend({
		tableName: 'players',
		idAttribute: 'player_id',
		initialize: function(obj, args) {
		},
		histories: function() {
			return this.hasMany(Database.Models.PlayerHistory,'player_id');
		},
		bans: function() {
			return this.hasMany(Database.Models.Ban,'player_id');
		},
	});
	
	Database.Collections.Players = db.Collection.extend({
		model: Database.Models.Player,
	}); 

	// ------------------------ PLAYERHISTORIES TABLE ------------------------
	tables.push({
		Name:'playerhistories',
		Create:function(t){
			t.increments('history_id').primary();
			t.integer('player_id');
			t.integer('server_id');			// The server he connected on
			t.string('player_name');			// The name he used to connect
			t.dateTime('connect_datetime');		// The datetime when he connected
			t.string('ip');		// The ip he used for connecting
		},
	});
	
	Database.Models.PlayerHistory = db.Model.extend({
		tableName: 'playerhistories',
		idAttribute: 'history_id',
		initialize: function(obj, args) {
		},
	});
	
	Database.Collections.PlayerHistories = db.Collection.extend({
		model: Database.Models.PlayerHistory,
	}); 

	// ------------------------ BANS TABLE ------------------------
	tables.push({
		Name:'bans',
		Create:function(t){
			t.increments('ban_id').primary();
			t.integer('player_id');			// The player who's banned
			t.integer('user_id');			// The user who banned him
			t.string('reason');			// The reason given for the ban
			t.dateTime('ban_datetime');		// The datetime when he was banned
			t.string('user_ip');		// The user ip who made the ban
			t.boolean('is_lifted'); 		// Whether or not the ban is still active
			t.integer('user_lift_id');			// The user who removed the ban
			t.string('user_lift_ip');		// The user ip who lifted the ban
			t.dateTime('lifted_datetime');		// The datetime when the ban was lifted
		},
	});
	
	Database.Models.Ban = db.Model.extend({
		tableName: 'bans',
		idAttribute: 'ban_id',
		initialize: function(obj, args) {
		},
		user: function() {
			return this.belongsTo(Database.Models.User,'user_id');
		},
	});
	
	Database.Collections.Bans = db.Collection.extend({
		model: Database.Models.Ban,
	}); 

	// ------------------------ MEMBERRIGHTS TABLE ------------------------
	tables.push({
		Name:'memberrights',
		Create:function(t){
			t.increments('memberrights_id').primary();
			t.integer('member_id');			    // The id of the member the rights belong to
			t.integer('server_id');			    // The server id the rights apply to
			t.string('rights_list');			// The list of the player rights
			t.dateTime('last_datetime');		// The datetime when it was last updated
		},
	});
	
	Database.Models.MemberRight = db.Model.extend({
		tableName: 'memberrights',
		idAttribute: 'memberrights_id',
		initialize: function(obj, args) {
		},
		member: function() {
			return this.belongsTo(Database.Models.Member,'member_id');
		},
	});
	
	Database.Collections.MemberRights = db.Collection.extend({
		model: Database.Models.MemberRight,
	}); 

	// ------------------------ MEMBERS TABLE ------------------------
	tables.push({
		Name:'members',
		Create:function(t){
			t.increments('member_id').primary();
			t.string('member_name');			// The name used by the player
			t.string('member_rank');			// The rank of the member in the team
			t.integer('membership_id');			// The group of the member
			t.string('game_id');		        // The id of the player in the game
			t.string('data');		            // The id of the player in the game
			t.dateTime('last_datetime');		// The datetime when he was last updated
		},
	});
	
	Database.Models.Member = db.Model.extend({
		tableName: 'members',
		idAttribute: 'member_id',
		initialize: function(obj, args) {
		},
		rights: function() {
			return this.hasMany(Database.Models.MemberRight,'member_id');
		},
	});
	
	Database.Collections.Members = db.Collection.extend({
		model: Database.Models.Member,
	}); 




	// --------------------------------------------------------------------------------------------
	//									INTERNAL METHODS
	// --------------------------------------------------------------------------------------------
	
	var Initialized = function(err,success) {
		// log('database.js: initialized: ' + success + ' - ' + initializing);
		if (!success) {
			// log('database.js: adding error: ' + err);
			Database.Failures++;
			errors.add(err);
		};
		initializing--;
		if (initializing === 0) {
			if (errors.Count == 0) {
				// log('database.js: all tables created without errors');
				successHanlder();
			} else {
				// log('database.js: tables created with errors');
				failureHanlder(errors);
			};
		};		
	};
	var CreateTable = function(table) {
		initializing++;
		// log('database.js: checking table: ' + table.Name + ' - ' + initializing);
		db.knex.schema.hasTable(table.Name).then(function(exists) {
			if (!exists) {
				// log('database.js: creating table: ' + table.Name);
				db.knex.schema.createTable(table.Name, table.Create).then(function () {
						log('database.js: ' + table.Name + ' table is created!');
						Initialized(null,true);
					});
			} else {
				// log('database.js: table exists: ' + table.Name);
				Initialized(null,true);
			};
		});
	};
	var CreateTables = function() {
		// log(tables);
		for (index = 0; index < tables.length; ++index) {
			CreateTable(tables[index]);
		};
	};
}).call(this);